/**
  ******************************************************************************
  * @file    mg_driver_mtp.c
  * @author  
  * @version V0.1
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; COPYRIGHT 2020 Shanghai Macrogiga Electronics</center></h2>
  *
  ******************************************************************************
  */
#include "mg_driver_mtp.h"

volatile unsigned int *c_CCR = ((unsigned int *)0x40007000);
volatile unsigned int *c_SR  = ((unsigned int *)0x40007004);
#define MTP_TIMEOUT_COUNT       (5*1000000)

static void MtpCalibrationSector(unsigned long SectorAddr)
{
    int mtp_time_out;
    
    MTP_CTRL->WDATA_REG = 0xffffffff;
    
    MTP_CTRL->CMD_REG = 0x58000000 | (SectorAddr >> 2);
    
    mtp_time_out = MTP_TIMEOUT_COUNT;
    while((mtp_time_out--) && (MTP_CTRL->STS != MTP_OPT_DONE)); //wait
    
    MTP_CTRL->CMD_REG = 0x58000000 | (SectorAddr >> 2) | 0x20;
    
    mtp_time_out = MTP_TIMEOUT_COUNT;
    while((mtp_time_out--) && (MTP_CTRL->STS != MTP_OPT_DONE)); //wait
}

static int PreProgramPage (unsigned long adr, unsigned long sz)
{
    // Address must be Word aligned
    int i;
    int mtp_time_out;

    if (adr & 3) {
        return (1); //add error
    }
    
    for(i = 0 ; i < sz ; i += 4)
    {   
        mtp_time_out = MTP_TIMEOUT_COUNT;
        
        MTP_CTRL->WDATA_REG = 0xffffffff;  /*write all 1 for target area*/
        
        MTP_CTRL->CMD_REG = ((adr >> 2) | MTP_CTRL_PROGRAM);
        while((mtp_time_out--) && (MTP_CTRL->STS != MTP_OPT_DONE)); //wait
        
        adr += 4;
    }
    
    return (0);   // Success
}

void EraseChip(void)
{
    int addr;
    
    for(addr = 0 ; addr < 32 * 1024 - 512 ; addr += 256)
    {
        MtpCalibrationSector(addr);
        PreProgramPage(addr,256);
    }
    
    MTP_CTRL->CMD_REG = (MTP_CTRL_CHIP_ERASE);  
    while(MTP_CTRL->STS != MTP_OPT_DONE); //wait, about 100ms
    
    while(1);
}


/*
 *  Erase Sector in Flash Memory
 *    Parameter:      adr:  Sector Address
 *    Return Value:   0 - OK,  1 - Failed
 */
int EraseSector(unsigned long adr)
{
    int i,mtp_time_out = MTP_TIMEOUT_COUNT;

    //erase of sector(size = 256)
    if (adr & (FLASH_SECTOR_SIZE - 1))
    {
        return (1); //error addr
    }
    
    *c_CCR = (*c_CCR) & (~0x0000001);
    MtpCalibrationSector(adr);
    
    PreProgramPage(adr,FLASH_SECTOR_SIZE); //pre-program to all 1's.
    
    MTP_CTRL->CMD_REG = ((adr >> 2) | MTP_CTRL_SECTOR_ERASE);
    
    while((mtp_time_out--) && (MTP_CTRL->STS != MTP_OPT_DONE)); //wait, about 100ms
    
    *c_CCR = 0x08;
    *c_CCR = 0x0C;
    i=200;while((!((*c_SR)&0x10)) && i)i--;
    *c_CCR = 0x0D;

    return (0);   // Success
}


/*
 *  Program Page in Flash Memory
 *    Parameter:      adr:  Page Start Address
 *                    sz:   Page Size
 *                    buf:  Page Data
 *    Return Value:   0 - OK,  1 - Failed
 */
int ProgramPage (unsigned long adr, unsigned long sz, unsigned char *buf)
{
    // Address must be Word aligned
    int i;
    unsigned long tv;
    int mtp_time_out;

    if ((adr | sz) & 3) {
        return (1); //add or size error: must be 4 bytes aligned
    }

    *c_CCR = (*c_CCR) & (~0x0000001);
    for(i = 0 ; i < sz ; i += 4)
    {   
        mtp_time_out = MTP_TIMEOUT_COUNT;
        
        tv  = buf[3]; tv <<= 8;
        tv |= buf[2]; tv <<= 8;
        tv |= buf[1]; tv <<= 8;
        tv |= buf[0]; 
        buf += 4;
        MTP_CTRL->WDATA_REG = tv;
        
        MTP_CTRL->CMD_REG = ((adr >> 2) | MTP_CTRL_PROGRAM);
        while((mtp_time_out--) && (MTP_CTRL->STS != MTP_OPT_DONE)); //wait
        
        adr += 4;
    }
    
    *c_CCR = 0x08;
    *c_CCR = 0x0C;
    i=200;while((!((*c_SR)&0x10)) && i)i--;
    *c_CCR = 0x0D;

    return (0);   // Success
}
